/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#ifndef __UMC_AVS_ENC_BIT_STREAM_H
#define __UMC_AVS_ENC_BIT_STREAM_H

#include "ippdefs.h"
#include "umc_avs_context.h"

namespace UMC
{

//
// Entropy encoding functions
//

inline
void InitializeEncBitStream(AVS_BIT_STREAM_CONTEXT *pCtx,
                            Ipp32u *pDst,
                            size_t dstSize)
{
    pCtx->dst = pDst;
    *(pCtx->dst) = 0;
    pCtx->dstEnd = pDst + dstSize;
    pCtx->offset = 32;

} // void InitializeEncBitStream(AVS_BIT_STREAM_CONTEXT *pCtx,

inline
void PutBit(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u bit)
{
    // decrement available bits
    pCtx->offset -= 1;

    // save bit to the register
    *(pCtx->dst) |= (bit & 1) << pCtx->offset;

    // update variables
    if (0 >= pCtx->offset)
    {
        pCtx->dst += 1;
        *(pCtx->dst) = 0;
        pCtx->offset = 32;
    }

} // void PutBit(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u bit)

inline
void PutBits(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u bits, Ipp32s numBits)
{
    if (numBits < pCtx->offset)
    {
        // decrement available bits
        pCtx->offset -= numBits;

        // save bit to the register
        *(pCtx->dst) |= (bits & ~(-1 << numBits)) << pCtx->offset;
    }
    else if (numBits == pCtx->offset)
    {
        // save bit to the register
        *(pCtx->dst) |= bits & ~(-1 << numBits);

        pCtx->dst += 1;
        *(pCtx->dst) = 0;
        pCtx->offset = 32;
    }
    else
    {
        Ipp32u preBits;

        // split bits into 2 sequences
        preBits = bits >> (numBits - pCtx->offset);
        numBits -= pCtx->offset;

        // save bit to the register
        *(pCtx->dst) |= preBits & ~(-1 << pCtx->offset);
        // advance pointer
        pCtx->dst += 1;
        *(pCtx->dst) = 0;
        pCtx->offset = 32 - numBits;
        // save remain bits
        *(pCtx->dst) |= (bits & ~(-1 << numBits)) << pCtx->offset;
    }

} // void PutBits(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u bits, Ipp32s numBits)

inline
void PutSignedBits(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u bits, Ipp32s numBits)
{
    PutBits(pCtx, bits, numBits);

} // void PutSignedBits(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u bits, Ipp32s numBits)

inline
void PutExpGolombCode(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u code)
{
    Ipp32s leadingZeroBits = 0;
    Ipp32s zeroMask = (-1 << 1);

    // update code
    code += 1;

    while (code & zeroMask)
    {
        // increment number of zeros
        leadingZeroBits += 1;
        // shift zero mask
        zeroMask <<= 1;
    }

    // put leading zeros
    PutBits(pCtx, 0, leadingZeroBits);

    // save meaning bits
    PutBits(pCtx, code, leadingZeroBits + 1);

} // void PutExpGolombCode(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u code)

inline
Ipp32u GetExpGolombCodeSize(Ipp32u code)
{
    Ipp32s leadingZeroBits = 0;
    Ipp32s zeroMask = (-1 << 1);

    // update code
    code += 1;

    while (code & zeroMask)
    {
        // increment number of zeros
        leadingZeroBits += 1;
        // shift zero mask
        zeroMask <<= 1;
    }

    return leadingZeroBits * 2 + 1;

} // Ipp32u GetExpGolombCodeSize(Ipp32u code)

inline
void PutUE(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u element)
{
    return PutExpGolombCode(pCtx, element);

} // void PutUE(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u element)

inline
Ipp32u GetUESize(Ipp32u element)
{
    return GetExpGolombCodeSize(element);

} // Ipp32u GetUESize(Ipp32u element)

inline
void PutSE(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32s element)
{
    Ipp32u code;

    if (element)
    {
        Ipp32s signExtended;

        // get element's sign
        signExtended = element >> 31;
        // we use simple scheme of abs calculation,
        // using one ADD and one XOR operation
        element = (element ^ signExtended) - signExtended;

        // create code
        code = (element << 1) - 1 - signExtended;
    }
    else
    {
        code = 0;
    }

    // put Exp-Glolomb code
    PutExpGolombCode(pCtx, code);

} // void PutSE(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32s element)

inline
Ipp32u GetSESize(Ipp32s element)
{
    if (element)
    {
        Ipp32u code;
        Ipp32s signExtended;

        // get element's sign
        signExtended = element >> 31;
        // we use simple scheme of abs calculation,
        // using one ADD and one XOR operation
        element = (element ^ signExtended) - signExtended;

        // create code
        code = (element << 1) - 1 - signExtended;

        // put Exp-Glolomb code
        return GetExpGolombCodeSize(code);
    }
    else
    {
        return 1;
    }

} // Ipp32u GetSESize(Ipp32s element)

inline
void PutExpGolombCode(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u code, Ipp32s order)
{
    Ipp32s leadingZeroBits = 0;
    Ipp32s zeroMask = (-1 << (order + 1));

    // update code
    code += (1 << order);

    while (code & zeroMask)
    {
        // increment number of zeros
        leadingZeroBits += 1;
        // shift zero mask
        zeroMask <<= 1;
    }

    // put leading zeros
    PutBits(pCtx, 0, leadingZeroBits);

    // save meaning bits
    PutBits(pCtx, code, leadingZeroBits + order + 1);

} // void PutExpGolombCode(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u code, Ipp32s order)

inline
Ipp32u GetExpGolombCodeSize(Ipp32u code, Ipp32s order)
{
    Ipp32s leadingZeroBits = 0;
    Ipp32s zeroMask = (-1 << (order + 1));

    // update code
    code += (1 << order);

    while (code & zeroMask)
    {
        // increment number of zeros
        leadingZeroBits += 1;
        // shift zero mask
        zeroMask <<= 1;
    }

    return leadingZeroBits * 2 + order + 1;

} // Ipp32u GetExpGolombCodeSize(Ipp32u code, Ipp32s order)

inline
void PutUE(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u element, Ipp32s order)
{
    return PutExpGolombCode(pCtx, element, order);

} // void PutUE(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32u element, Ipp32s order)

inline
Ipp32u GetUESize(Ipp32u element, Ipp32s order)
{
    return GetExpGolombCodeSize(element, order);

} // Ipp32u GetUESize(Ipp32u element, Ipp32s order)

inline
void PutSE(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32s element, Ipp32s order)
{
    Ipp32u code;

    if (element)
    {
        Ipp32s signExtended;

        // get element's sign
        signExtended = element >> 31;
        // we use simple scheme of abs calculation,
        // using one ADD and one XOR operation
        element = (element ^ signExtended) - signExtended;

        // create code
        code = (element << 1) - 1 - signExtended;
    }
    else
    {
        code  = 0;
    }

    // put Exp-Glolomb code
    PutExpGolombCode(pCtx, code, order);

} // void PutSE(AVS_BIT_STREAM_CONTEXT *pCtx, Ipp32s element, Ipp32s order)

inline
Ipp32u GetSESize(Ipp32s element, Ipp32s order)
{
    if (element)
    {
        Ipp32u code;
        Ipp32s signExtended;

        // get element's sign
        signExtended = element >> 31;
        // we use simple scheme of abs calculation,
        // using one ADD and one XOR operation
        element = (element ^ signExtended) - signExtended;

        // create code
        code = (element << 1) - 1 - signExtended;

        // put Exp-Glolomb code
        return GetExpGolombCodeSize(code, order);
    }
    else
    {
        return order + 1;
    }

} // Ipp32u GetSESize(Ipp32s element, Ipp32s order)

inline
bool IsThereMoreSpace(AVS_BIT_STREAM_CONTEXT *pCtx)
{
    return (pCtx->dst >= pCtx->dstEnd);

} // bool IsThereMoreSpace(AVS_BIT_STREAM_CONTEXT *pCtx)

inline
void FlushBitStream(AVS_BIT_STREAM_CONTEXT *pCtx)
{
    // set the new dword
    pCtx->dst += 1;
    pCtx->offset = 32;

} // void FlushBitStream(AVS_BIT_STREAM_CONTEXT *pCtx)

// forward declaration of used types
struct AVS_SEQUENCE_HEADER;
struct AVS_PICTURE_HEADER;
struct AVS_SLICE_HEADER;

// declaration of headers' parsing routines
extern
void EncodeSeqHeader(AVS_BIT_STREAM_CONTEXT *pCtx,
                     const AVS_SEQUENCE_HEADER *pSeqHeader);
extern
void EncodePicHeader(AVS_BIT_STREAM_CONTEXT *pCtx,
                     const AVS_SEQUENCE_HEADER *pSeqHeader,
                     const AVS_PICTURE_HEADER *pPicHeader);
extern
void EncodeSlcHeader(AVS_BIT_STREAM_CONTEXT *pCtx,
                     const AVS_SEQUENCE_HEADER *pSeqHeader,
                     const AVS_PICTURE_HEADER *pPicHeader,
                     const AVS_SLICE_HEADER *pSlcHeader);
/*
extern
Ipp32s GetFieldNum(Ipp32u *pSlcData,
                   AVS_SEQUENCE_HEADER *pSeqHeader,
                   AVS_PICTURE_HEADER *pPicHeader);
*/
} // namespace UMC

#endif // __UMC_AVS_ENC_BIT_STREAM_H
